package com.info.zhiduoduo.common.utils;

import com.alibaba.fastjson.JSONObject;

import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

public class RSAUtil {

	/**
	 * 算法
	 */
	public static final String RSA = "RSA";

	/**
	 * 公钥Map Key
	 */
	public static final String PUBLIC_KEY = "publicKey";

	/**
	 * 私钥Map Key
	 */
	public static final String PRIVATE_KEY = "privateKey";

	/**
	 * 使用RSA公钥加密数据
	 *
	 * @param data      原始数据
	 * @param publicKey 公钥
	 * @return 密文
	 * @throws Exception
	 */
	public static String encrypt(String data, String publicKey) throws Exception {
		RSAPublicKey pbk = genPublicKey(publicKey);

		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
		cipher.init(Cipher.ENCRYPT_MODE, pbk);

		byte[] encDate = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));

		return Base64.getEncoder().encodeToString(encDate);
	}

	/**
	 * 使用RSA私钥解密数据
	 *
	 * @param data       密文
	 * @param privateKey 私钥字符串
	 * @return 明文
	 * @throws Exception
	 */
	public static String decrypt(String data, String privateKey) throws Exception {
		RSAPrivateKey pvk = genPrivateKey(privateKey);
		return decrypt(data, pvk);
	}

	/**
	 * 使用RSA私钥解密数据
	 *
	 * @param data     密文
	 * @param pfxPath  pfx证书文件路径
	 * @param password pfx证书文件密码
	 * @return 明文
	 * @throws Exception
	 */
	public static String decrypt(String data, String pfxPath, String password) throws Exception {
		RSAPrivateKey pvk = loadPrivateKey(pfxPath, password);
		return decrypt(data, pvk);
	}

	private static String decrypt(String data, RSAPrivateKey privateKey) throws Exception {
		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		byte[] btSrc = cipher.doFinal(Base64.getDecoder().decode(data));
		return new String(btSrc, StandardCharsets.UTF_8);
	}

	/**
	 * 通过公钥字符串生成公钥对象
	 *
	 * @param publicKey
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeySpecException
	 */
	private static RSAPublicKey genPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
		return (RSAPublicKey) KeyFactory.getInstance(RSA).generatePublic(keySpec);
	}

	/**
	 * 通过私钥字符串生成私钥对象
	 *
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	private static RSAPrivateKey genPrivateKey(String privateKey) throws Exception {
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
		return (RSAPrivateKey) KeyFactory.getInstance(RSA).generatePrivate(keySpec);
	}

	/**
	 * 从证书文件中直接获取私钥对象
	 *
	 * @param pfxPath
	 * @param password
	 * @return
	 * @throws Exception
	 */
	private static RSAPrivateKey loadPrivateKey(String pfxPath, String password) throws Exception {
		KeyStore ks = KeyStore.getInstance("PKCS12");
		FileInputStream fis = new FileInputStream(pfxPath);
		char[] nPassword;
		if (password == null || password.trim().equals("")) {
			nPassword = null;
		} else {
			nPassword = password.toCharArray();
		}
		ks.load(fis, nPassword);
		fis.close();
		Enumeration enumas = ks.aliases();
		String keyAlias = null;
		if (enumas.hasMoreElements()) {
			keyAlias = (String) enumas.nextElement();
		}
		return (RSAPrivateKey) ks.getKey(keyAlias, nPassword);
	}

	/**
	 * 通过PFX证书获取RSA公钥和私钥
	 *
	 * @param pfxPath  证书存放路径
	 * @param password 证书密码
	 * @return
	 */
	public static Map<String, String> loadKeyByPfxFile(String pfxPath, String password) throws Exception {
		KeyStore ks = KeyStore.getInstance("PKCS12");
		FileInputStream fis = new FileInputStream(pfxPath);
		char[] nPassword;
		if (password == null || password.trim().equals("")) {
			nPassword = null;
		} else {
			nPassword = password.toCharArray();
		}
		ks.load(fis, nPassword);
		fis.close();
		Enumeration enumas = ks.aliases();
		String keyAlias = null;
		if (enumas.hasMoreElements()) {
			keyAlias = (String) enumas.nextElement();
		}
		PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);
		Certificate cert = ks.getCertificate(keyAlias);
		PublicKey pubkey = cert.getPublicKey();
		String publicKey = Base64.getEncoder().encodeToString(pubkey.getEncoded());
		String privateKey = Base64.getEncoder().encodeToString(prikey.getEncoded());
		Map<String, String> keyMap = new HashMap<>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}

	public static void main(String[] args) throws Exception {
		// RSA证书文件路径
		String pfxPath = "/Users/eva/bryan/项目文档/ /keystore/mykey.pfx";
		// RSA证书文件密码，需要此密码从证书文件中解析出私钥和公钥
		String password = "111111";

		Map<String, String> map = RSAUtil.loadKeyByPfxFile(pfxPath, password);
		String privateKey = map.get(PRIVATE_KEY);
		String publicKey = map.get(PUBLIC_KEY);
		System.out.println("私钥=" + privateKey);
		System.out.println("公钥=" + publicKey);

		// 一、客户平台加密请求参数步骤：
		// 接口对应的请求对象
		String content = "{\"acctname\": \"全渠道\",\"acctno\": \"6216261000000000018\",\"accttype\": 1,\"bankname\": \"平安银行\",\"bankno\":\"343123455\"}";
		// a.获取16位随机字符串，作为秘钥key
		String key = RandomUtil.getRandomStr(16);
		// b.对请求参数进行AES加密
		String data = AesUtil.encrypt(content, key);
		System.out.println("请求对象密文：" + data);
		// c.对秘钥key进行RSA加密
		String encryptKey = RSAUtil.encrypt(key, publicKey);
		System.out.println("encryptKey:" + encryptKey);
		// 模拟接口请求参数body
		JSONObject requestBody = new JSONObject();
		requestBody.put("merchantNo", "1000001");
		requestBody.put("version", "1.0");
		requestBody.put("key", encryptKey);
		requestBody.put("data", data);
		System.out.println("requestBody:" + requestBody.toJSONString());

		// 解密应答参数步骤
		// 模拟应答参数body
		JSONObject responseBody = new JSONObject();
		responseBody.put("merchantNo", "1000001");
		responseBody.put("version", "1.0");
		responseBody.put("key",
				"FIRez5hE7WItpiAjk44D2BS8l5MbksQVnMwLYls2Dum8BKljjbg3wLnbaNdUqJAcTESo2M0Babg2T0iG1PBhH2wInM5ktxpu0a7CBCa++9Cbj0A8T1v/ZY54NRDy1oLOxlvzNRc8U6K6nuVej5oKU5FVgYx514ZLUQizlcq2N8g=");
		responseBody.put("data", "DiE/g4blfMG/UfbmItgyXw==");
		// a.对应答参数中的key字段进行RSA解密（使用RSA私钥），获取解密应答对象密文的秘钥key
		// String decryptKey = RSAUtils.decrypt(responseBody.getString("key"), pfxPath, password);
		String decryptKey = RSAUtil.decrypt(responseBody.getString("key"), privateKey);
		System.out.println("decryptKey:" + decryptKey);
		// b.使用此秘钥key，对应答参数中的data字段进行AES解密，获取真正的应答结果对象（JSON字符串）
		String contentdecrypt = AesUtil.decrypt(responseBody.getString("data"), decryptKey);
		System.out.println("decryptContent:" + contentdecrypt);
	}
}
